home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / osrc.arc / SMTPSERV.C < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-30  |  17.0 KB  |  780 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  */
  4. #include <stdio.h>
  5. #include <time.h>
  6. #ifdef UNIX
  7. #include <sys/types.h>
  8. #endif
  9. #include <ctype.h>
  10. #include <setjmp.h>
  11. #include "global.h"
  12. #include "mbuf.h"
  13. #include "cmdparse.h"
  14. #include "socket.h"
  15. #include "proc.h"
  16. #include "smtp.h"
  17.  
  18. extern char Nospace[];
  19. static struct smtpsv *mail_create();
  20. static char *getname();
  21. static void mail_clean();
  22. static int validate_address();
  23. static void smtpserv();
  24. int queuejob(), smtp0(), smtp1();
  25. long get_msgid();
  26. struct list *expandalias();
  27. char *getenv();
  28. struct tm *localtime();
  29. FILE *tmpfile();
  30. long atol();
  31.  
  32. /* Command table */
  33. static char *commands[] = {
  34.     "helo",
  35. #define    HELO_CMD    0
  36.     "noop",
  37. #define    NOOP_CMD    1
  38.     "mail from:",
  39. #define    MAIL_CMD    2
  40.     "quit",
  41. #define    QUIT_CMD    3
  42.     "rcpt to:",
  43. #define    RCPT_CMD    4
  44.     "help",
  45. #define    HELP_CMD    5
  46.     "data",
  47. #define    DATA_CMD    6
  48.     "rset",
  49. #define    RSET_CMD    7
  50.     NULLCHAR
  51. };
  52.  
  53. /* Reply messages */
  54. static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
  55. static char banner[] = "220 %s SMTP ready\r\n";
  56. static char closing[] = "221 Closing\r\n";
  57. static char ok[] = "250 Ok\r\n";
  58. static char reset[] = "250 Reset state\r\n";
  59. static char sent[] = "250 Sent\r\n";
  60. static char ourname[] = "250 %s, Share and Enjoy!\r\n";
  61. static char enter[] = "354 Enter mail, end with .\r\n";
  62. static char ioerr[] = "452 Temp file write error\r\n";
  63. static char badcmd[] = "500 Command unrecognized\r\n";
  64. static char syntax[] = "501 Syntax error\r\n";
  65. static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
  66. static char unknown[] = "550 <%s> address unknown\r\n";
  67.  
  68. static int Ssmtp = -1; /* prototype socket for service */
  69.  
  70. /* Start up SMTP receiver service */
  71. int
  72. smtp1(argc,argv)
  73. int argc;
  74. char *argv[];
  75. {
  76.     struct sockaddr_in lsocket;
  77.     int s;
  78.  
  79.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  80.     chname(Curproc,"SMTP listener");
  81.  
  82.     lsocket.sin_family = AF_INET;
  83.     lsocket.sin_addr.s_addr = Ip_addr;
  84.     if(argc < 2)
  85.         lsocket.sin_port = IPPORT_SMTP;
  86.     else
  87.         lsocket.sin_port = atoi(argv[1]);
  88.  
  89.     Ssmtp = socket(AF_INET,SOCK_STREAM,0);
  90.     bind(Ssmtp,(char *)&lsocket,sizeof(lsocket));
  91.     listen(Ssmtp,1);
  92.     for(;;){
  93.         if((s = accept(Ssmtp,NULLCHAR,(int *)NULL)) == -1)
  94.             break;    /* Service is shutting down */
  95.  
  96.         /* Spawn a server */
  97.         newproc("SMTP server",2048,smtpserv,s,NULL,NULL);
  98.     }
  99.     return 0;
  100. }
  101.  
  102. /* Shutdown SMTP service (existing connections are allowed to finish) */
  103. int
  104. smtp0()
  105. {
  106.     if (Ssmtp != -1){
  107.         close_s(Ssmtp);
  108.         Ssmtp = -1;
  109.     }
  110.     return 0;
  111. }
  112.  
  113. static void
  114. smtpserv(s,unused)
  115. int s;
  116. void *unused;
  117. {
  118.     struct smtpsv *mp;
  119.     char **cmdp,buf[LINELEN],*arg,*cp,*cmd;
  120.     int cnt;
  121.     char address_type;
  122.  
  123.     sockowner(s,Curproc);        /* We own it now */
  124.     log(s,"open SMTP");
  125.  
  126.     if((mp = mail_create()) == NULLSMTPSV){
  127.         printf(Nospace);
  128.         log(s,"close SMTP - no space");
  129.         close_s(s);
  130.         return;
  131.     }
  132.     mp->s = s;
  133.  
  134.     (void) usprintf(s,banner,Hostname);
  135.  
  136. loop:    if ((cnt = recvline(s,buf,sizeof(buf))) == -1) {
  137.         /* He closed on us */
  138.         goto quit;
  139.     }
  140.     if(cnt < 4){
  141.         /* Can't be a legal command */
  142.         usprintf(mp->s,badcmd);
  143.         goto loop;
  144.     }    
  145.     rip(buf);
  146.     cmd = buf;
  147.  
  148.     /* Translate entire buffer to lower case */
  149.     for(cp = cmd;*cp != '\0';cp++)
  150.         *cp = tolower(*cp);
  151.  
  152.     /* Find command in table; if not present, return syntax error */
  153.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  154.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  155.             break;
  156.     if(*cmdp == NULLCHAR){
  157.         (void) usprintf(mp->s,badcmd);
  158.         return;
  159.     }
  160.     arg = &cmd[strlen(*cmdp)];
  161.     /* Skip spaces after command */
  162.     while(*arg == ' ')
  163.         arg++;
  164.     /* Execute specific command */
  165.     switch(cmdp-commands) {
  166.     case HELO_CMD:
  167.         free(mp->system);
  168.         if((mp->system = strdup(arg)) == NULLCHAR){
  169.             /* If the system is out of memory, just close */
  170.             goto quit;
  171.         } else {
  172.             (void) usprintf(mp->s,ourname,Hostname);
  173.         }
  174.         break;
  175.     case NOOP_CMD:
  176.         (void) usprintf(mp->s,ok);
  177.         break;
  178.     case MAIL_CMD:
  179.         if((cp = getname(arg)) == NULLCHAR){
  180.             (void) usprintf(mp->s,syntax);
  181.             break;
  182.         }
  183.         free(mp->from);
  184.         if((mp->from = strdup(cp)) == NULLCHAR){
  185.             /* If the system is out of memory, just close */
  186.             goto quit;
  187.         } else {
  188.             (void) usprintf(mp->s,ok);
  189.         }
  190.         break;
  191.     case QUIT_CMD:
  192.         (void) usprintf(mp->s,closing);
  193.         goto quit;
  194.         break;
  195.     case RCPT_CMD:    /* Specify recipient */
  196.         if((cp = getname(arg)) == NULLCHAR){
  197.             (void) usprintf(mp->s,syntax);
  198.             break;
  199.         }
  200.  
  201.         /* check if address is ok */
  202.         if ((address_type = validate_address(cp)) == BADADDR) {
  203.             (void) usprintf(mp->s,unknown,cp);
  204.             break;
  205.         }
  206.         /* if a local address check for an alias */
  207.         if (address_type == LOCAL)
  208.             expandalias(&mp->to, cp);
  209.         else
  210.             /* a remote address is added to the list */
  211.             addlist(&mp->to, cp, address_type);
  212.  
  213.         (void) usprintf(mp->s,ok);
  214.         break;
  215.     case HELP_CMD:
  216.         (void) usprintf(mp->s,help);
  217.         break;
  218.     case DATA_CMD:
  219.         if(mp->to == NULLLIST)
  220.             (void) usprintf(mp->s,needrcpt);
  221.         else if ((mp->data = tmpfile()) == NULLFILE)
  222.             (void) usprintf(mp->s,ioerr);
  223.          else
  224.             getmsgtxt(mp);
  225.         break;
  226.     case RSET_CMD:
  227.         del_list(mp->to);
  228.         mp->to = NULLLIST;
  229.         (void) usprintf(mp->s,reset);
  230.         break;
  231.     }
  232.     goto loop;
  233.  
  234. quit:
  235.     log(mp->s,"close SMTP");
  236.     close_s(mp->s);
  237.     mail_clean(mp);
  238. }
  239.  
  240. /* read the message text */
  241. int
  242. getmsgtxt(mp)
  243. struct smtpsv *mp;
  244. {
  245.     char buf[LINELEN];
  246.     register char *p = buf;
  247.     long t;
  248.  
  249.     /* Add timestamp; ptime adds newline */
  250.     time(&t);
  251.     fprintf(mp->data,"Received: ");
  252.     if(mp->system != NULLCHAR)
  253.         fprintf(mp->data,"from %s ",mp->system);
  254.     fprintf(mp->data,"by %s with SMTP\n\tid AA%ld ; %s",
  255.             Hostname, get_msgid(), ptime(&t));
  256.     if(ferror(mp->data)){
  257.         (void) usprintf(mp->s,ioerr);
  258.         return 1;
  259.     } else {
  260.         (void) usprintf(mp->s,enter);
  261.     }
  262.     while(1) {
  263.         if(recvline(mp->s,p,sizeof(buf)) == -1){
  264.             return 1;
  265.         }
  266.         rip(p);
  267.         /* check for end of message ie a . or escaped .. */
  268.         if (*p == '.') {
  269.             if (*++p == '\0') {
  270.                 /* Also sends appropriate response */
  271.                 if (mailit(mp->data,mp->from,mp->to) != 0)
  272.                     (void) usprintf(mp->s,ioerr);
  273.                 else
  274.                     (void) usprintf(mp->s,sent);
  275.                 fclose(mp->data);
  276.                 mp->data = NULLFILE;
  277.                 del_list(mp->to);
  278.                 mp->to = NULLLIST;
  279.                 return 0;
  280.             } else if (!(*p == '.' && *(p+1) == '\0'))
  281.                 p--;
  282.         }
  283.         /* for UNIX mail compatiblity */
  284.         if (strncmp(p,"From ",5) == 0)
  285.             (void) putc('>',mp->data);
  286.         /* Append to data file */
  287.         if(fprintf(mp->data,"%s\n",p) < 0) {
  288.             (void) usprintf(mp->s,ioerr);
  289.             return 1;
  290.         }
  291.  
  292.     }
  293.     return 0;
  294. }
  295.  
  296. /* Create control block, initialize */
  297. static struct smtpsv *
  298. mail_create()
  299. {
  300.     register struct smtpsv *mp;
  301.  
  302.     if((mp = (struct smtpsv *)calloc(1,sizeof(struct smtpsv))) == NULLSMTPSV)
  303.         return NULLSMTPSV;
  304.     return mp;
  305. }
  306.  
  307. /* Free resources, delete control block */
  308. static void
  309. mail_clean(mp)
  310. register struct smtpsv *mp;
  311. {
  312.     if (mp == NULLSMTPSV)
  313.         return;
  314.     free(mp->system);
  315.     free(mp->from);
  316.     if(mp->data != NULLFILE)
  317.         fclose(mp->data);
  318.     del_list(mp->to);
  319.     free((char *)mp);
  320. }
  321.  
  322.  
  323. /* Given a string of the form <user@host>, extract the part inside the
  324.  * brackets and return a pointer to it.
  325.  */
  326. static char *
  327. getname(cp)
  328. register char *cp;
  329. {
  330.     register char *cp1;
  331.  
  332.     if ((cp = strchr(cp,'<')) == NULLCHAR)
  333.         return NULLCHAR;
  334.     cp++;    /* cp -> first char of name */
  335.     if ((cp1 = strchr(cp,'>')) == NULLCHAR)
  336.         return NULLCHAR;
  337.     *cp1 = '\0';
  338.     return cp;
  339. }
  340.  
  341.         
  342. /* General mailit function. It takes a list of addresses which have already
  343. ** been verified and expanded for aliases. Base on the current mode the message
  344. ** is place in an mbox, the outbound smtp queue or the rqueue interface
  345. */
  346. int
  347. mailit(data,from,tolist)
  348. FILE *data;
  349. char *from;
  350. struct list *tolist;
  351. {
  352.     register struct list *ap;
  353.     register FILE *fp;
  354.     int c;
  355.     char    mailbox[50];
  356.     char    *cp;
  357.     char    *desthost;
  358.     int    fail = 0;
  359.     time_t    t;
  360.  
  361.     if ((Smtpmode & QUEUE) != 0)
  362.         return(router_queue(data,from,tolist));
  363.  
  364.     for(ap = tolist;ap != NULLLIST;ap = ap->next) {
  365.  
  366.         rewind(data);
  367.  
  368.         /* non local mail queue it */
  369.         if (ap->type == DOMAIN) {
  370.             if ((desthost = strchr(ap->val,'@')) != NULLCHAR)
  371.                 desthost++;
  372.             fail = queuejob(data,desthost,ap->val,from);
  373.         } else {
  374.             /* strip off host name */
  375.             if ((cp = strchr(ap->val,'@')) != NULLCHAR)
  376.                 *cp = '\0';
  377.  
  378.             /* truncate long user names */
  379.             if (strlen(ap->val) > MBOXLEN)
  380.                 ap->val[MBOXLEN] = '\0';
  381.  
  382.             /* if mail file is busy save it in our smtp queue
  383.              * and let the smtp daemon try later.
  384.              */
  385.             if (mlock(Mailspool,ap->val))
  386.                 fail = queuejob(data,Hostname,ap->val,from);
  387.             else {
  388.                 sprintf(mailbox,"%s/%s.txt",Mailspool,ap->val);
  389.                 if((fp = fopen(mailbox,APPEND_TEXT)) != NULLFILE) {
  390.                     time(&t);
  391.                     fprintf(fp,
  392.                     "From %s %s",from,ctime(&t));
  393.                     while((c = getc(data)) != EOF)
  394.                         if(putc(c,fp) == EOF)
  395.                             break;
  396.                     if(ferror(fp))
  397.                         fail = 1;
  398.                     else
  399.                         fprintf(fp,"\n");
  400.                     /* Leave a blank line between msgs */
  401.                     fclose(fp);
  402.                     printf("New mail arrived for %s\n",ap->val);
  403.                 } else 
  404.                     fail = 1;
  405.                 (void) rmlock(Mailspool,ap->val);
  406.                 if (fail)
  407.                     break;
  408.                 smtplog("deliver: To: %s From: %s",ap->val,from);
  409.             }
  410.         }
  411.     }
  412.     return fail;
  413. }
  414.  
  415. /* Return Date/Time in Arpanet format in passed string */
  416. char *
  417. ptime(t)
  418. long *t;
  419. {
  420.     /* Print out the time and date field as
  421.      *        "DAY day MONTH year hh:mm:ss ZONE"
  422.      */
  423.     register struct tm *ltm;
  424.     static char tz[4];
  425.     static char str[40];
  426.     char *p;
  427.     static char *days[7] = {
  428.     "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
  429.  
  430.     static char *months[12] = {
  431.         "Jan","Feb","Mar","Apr","May","Jun",
  432.         "Jul","Aug","Sep","Oct","Nov","Dec" };
  433.  
  434.     /* Read the system time */
  435.     ltm = localtime(t);
  436.  
  437.     if (*tz == '\0')
  438.         if ((p = getenv("TZ")) == NULL)
  439.             strcpy(tz,"GMT");
  440.         else
  441.             strncpy(tz,p,3);
  442.  
  443.     /* rfc 822 format */
  444.     sprintf(str,"%s, %.2d %s %02d %02d:%02d:%02d %.3s\n",
  445.         days[ltm->tm_wday],
  446.         ltm->tm_mday,
  447.         months[ltm->tm_mon],
  448.         ltm->tm_year,
  449.         ltm->tm_hour,
  450.         ltm->tm_min,
  451.         ltm->tm_sec,
  452.         tz);
  453.     return(str);
  454. }
  455.  
  456. long 
  457. get_msgid()
  458. {
  459.     char sfilename[LINELEN];
  460.     char s[20];
  461.     register long sequence = 0;
  462.     FILE *sfile;
  463.  
  464.     sprintf(sfilename,"%s/sequence.seq",Mailqdir);
  465.     sfile = fopen(sfilename,READ_TEXT);
  466.  
  467.     /* if sequence file exists, get the value, otherwise set it */
  468.     if (sfile != NULL) {
  469.         (void) fgets(s,sizeof(s),sfile);
  470.         sequence = atol(s);
  471.     /* Keep it in range of and 8 digit number to use for dos name prefix. */
  472.         if (sequence < 0L || sequence > 99999999L )
  473.             sequence = 0;
  474.         fclose(sfile);
  475.     }
  476.  
  477.     /* increment sequence number, and write to sequence file */
  478.     sfile = fopen(sfilename,WRITE_TEXT);
  479.     fprintf(sfile,"%ld",++sequence);
  480.     fclose(sfile);
  481.     return sequence;
  482. }
  483.  
  484. #ifdef    MSDOS
  485. /* Illegal characters in a DOS filename */
  486. static char baddoschars[] = "\"[]:|<>+=;,";
  487. #endif
  488.  
  489. /* test if mail address is valid */
  490. static int
  491. validate_address(s)
  492. char *s;
  493. {
  494.     char *cp;
  495.     int32 addr;
  496.  
  497.  
  498.  
  499.     /* if address has @ in it the check dest address */
  500.     if ((cp = strrchr(s,'@')) != NULLCHAR) {
  501.         cp++;
  502.         /* 1st check if its our hostname
  503.         * if not then check the hosts file and see
  504.         * if we can resolve ther address to a know site
  505.         * or one of our aliases
  506.         */
  507.         if (strcmp(cp,Hostname) != 0) {
  508.             if ((addr = mailroute(cp)) == 0
  509.                 && (Smtpmode & QUEUE) == 0)
  510.                 return BADADDR;
  511.             if (addr != Ip_addr)
  512.                 return DOMAIN;
  513.         }
  514.         
  515.         /* on a local address remove the host name part */
  516.         *--cp = '\0';
  517.     }
  518.  
  519.     /* if using an external router leave address alone */
  520.     if ((Smtpmode & QUEUE) != 0)
  521.         return LOCAL;
  522.  
  523.     /* check for the user%host hack */
  524.     if ((cp = strrchr(s,'%')) != NULLCHAR) {
  525.         *cp = '@';
  526.         cp++;
  527.         /* reroute based on host name following the % seperator */
  528.         if (mailroute(cp) == 0)
  529.             return BADADDR;
  530.         else
  531.             return DOMAIN;
  532.     }
  533.  
  534. #ifdef MSDOS    /* dos file name checks */
  535.     /* Check for characters illegal in MS-DOS file names */
  536.     for(cp = baddoschars;*cp != '\0';cp++){
  537.         if(strchr(s,*cp) != NULLCHAR)
  538.             return BADADDR;    
  539.     }
  540. #endif
  541.     return LOCAL;
  542. }
  543.  
  544. /* place a mail job in the outbound queue */
  545. int
  546. queuejob(dfile,host,to,from)
  547. FILE *dfile;
  548. char *host,*to,*from;
  549. {
  550.     FILE *fp;
  551.     char tmpstring[50];
  552.     char prefix[9];
  553.     register int c;
  554.  
  555.     sprintf(prefix,"%ld",get_msgid());
  556.     smtplog("queue job %s To: %s From: %s",prefix,to,from);
  557.     mlock(Mailqdir,prefix);
  558.     sprintf(tmpstring,"%s/%s.txt",Mailqdir,prefix);
  559.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  560.         (void) rmlock(Mailqdir,prefix);
  561.         return 1;
  562.     }
  563.     while((c = getc(dfile)) != EOF)
  564.         if(putc(c,fp) == EOF)
  565.             break;
  566.     if(ferror(fp)){
  567.         fclose(fp);
  568.         (void) rmlock(Mailqdir,prefix);
  569.         return 1;
  570.     }
  571.     fclose(fp);
  572.     sprintf(tmpstring,"%s/%s.wrk",Mailqdir,prefix);
  573.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  574.         (void) rmlock(Mailqdir,prefix);
  575.         return 1;
  576.     }
  577.     fprintf(fp,"%s\n%s\n%s\n",host,from,to);
  578.     fclose(fp);
  579.     (void) rmlock(Mailqdir,prefix);
  580.     return 0;
  581. }
  582.  
  583. /* Deliver mail to the appropriate mail boxes */
  584. int
  585. router_queue(data,from,to)
  586. FILE *data;
  587. char *from;
  588. struct list *to;
  589. {
  590.     int c;
  591.     register struct list *ap;
  592.     FILE *fp;
  593.     char tmpstring[50];
  594.     char prefix[9];
  595.  
  596.     sprintf(prefix,"%ld",get_msgid());
  597.     mlock(Routeqdir,prefix);
  598.     sprintf(tmpstring,"%s/%s.txt",Routeqdir,prefix);
  599.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  600.         (void) rmlock(Routeqdir,prefix);
  601.         return 1;
  602.     }
  603.     rewind(data);
  604.     while((c = getc(data)) != EOF)
  605.         if(putc(c,fp) == EOF)
  606.             break;
  607.     if(ferror(fp)){
  608.         fclose(fp);
  609.         (void) rmlock(Routeqdir,prefix);
  610.         return 1;
  611.     }
  612.     fclose(fp);
  613.     sprintf(tmpstring,"%s/%s.wrk",Routeqdir,prefix);
  614.     if((fp = fopen(tmpstring,WRITE_TEXT)) == NULLFILE) {
  615.         (void) rmlock(Routeqdir,prefix);
  616.         return 1;
  617.     }
  618.     fprintf(fp,"From: %s\n",from);
  619.     for(ap = to;ap != NULLLIST;ap = ap->next) {
  620.         fprintf(fp,"To: %s\n",ap->val);
  621.     }
  622.     fclose(fp);
  623.     (void) rmlock(Routeqdir,prefix);
  624.     smtplog("rqueue job %s From: %s",prefix,from);
  625.     return 0;
  626. }
  627.  
  628. /* add an element to the front of the list pointed to by head 
  629. ** return NULLLIST if out of memory.
  630. */
  631. struct list *
  632. addlist(head,val,type)
  633. struct list **head;
  634. char *val;
  635. int type;
  636. {
  637.     register struct list *tp;
  638.  
  639.     tp = (struct list *)calloc(1,sizeof(struct list));
  640.     if (tp == NULLLIST)
  641.         return NULLLIST;
  642.  
  643.     tp->next = NULLLIST;
  644.  
  645.     /* allocate storage for the char string */
  646.     if ((tp->val = strdup(val)) == NULLCHAR) {
  647.         (void) free((char *)tp);
  648.         return NULLLIST;
  649.     }
  650.     tp->type = type;
  651.  
  652.     /* add entry to front of existing list */
  653.     if (*head == NULLLIST)
  654.         *head = tp;
  655.     else {
  656.         tp->next = *head;
  657.         *head = tp;
  658.     }
  659.     return tp;
  660.  
  661. }
  662.  
  663. #define SKIPWORD(X) while(*X && *X!=' ' && *X!='\t' && *X!='\n' && *X!= ',') X++;
  664. #define SKIPSPACE(X) while(*X ==' ' || *X =='\t' || *X =='\n' || *X == ',') X++;
  665.  
  666. /* check for and alias and expand alias into a address list */
  667. struct list *
  668. expandalias(head, user)
  669. struct list **head;
  670. char *user;
  671. {
  672.     FILE *fp;
  673.     register char *s,*p;
  674.     int inalias;
  675.     struct list *tp;
  676.     char buf[LINELEN];
  677.     
  678.         /* no alias file found */
  679.     if ((fp = fopen(Alias, READ_TEXT)) == NULLFILE)
  680.         return addlist(head, user, LOCAL);
  681.  
  682.     inalias = 0;
  683.     while (fgets(buf,LINELEN,fp) != NULLCHAR) {
  684.         p = buf;
  685.         if ( *p == '#' || *p == '\0')
  686.             continue;
  687.         rip(p);
  688.  
  689.         /* if not in an matching entry skip continuation lines */
  690.         if (!inalias && isspace(*p))
  691.             continue;
  692.  
  693.         /* when processing an active alias check for a continuation */
  694.         if (inalias) {
  695.             if (!isspace(*p)) 
  696.                 break;    /* done */
  697.         } else {
  698.             s = p;
  699.             SKIPWORD(p);
  700.             *p++ = '\0';    /* end the alias name */
  701.             if (strcmp(s,user) != 0)
  702.                 continue;    /* no match go on */
  703.             inalias = 1;
  704.         }
  705.  
  706.         /* process the recipients on the alias line */
  707.         SKIPSPACE(p);
  708.         while(*p != '\0' && *p != '#') {
  709.             s = p;
  710.             SKIPWORD(p);
  711.             if (*p != '\0')
  712.                 *p++ = '\0';
  713.  
  714.             /* find hostname */
  715.             if (strchr(s,'@') != NULLCHAR)
  716.                 tp = addlist(head,s,DOMAIN);
  717.             else
  718.                 tp = addlist(head,s,LOCAL);
  719.             SKIPSPACE(p);
  720.         }
  721.     }
  722.     (void) fclose(fp);
  723.  
  724.     if (inalias)    /* found and processed and alias. */
  725.         return tp;
  726.  
  727.     /* no alias found treat as a local address */
  728.     return addlist(head, user, LOCAL);
  729. }
  730.  
  731. /*VARARGS2*/
  732. void
  733. smtplog(fmt,arg1,arg2,arg3,arg4)
  734. char *fmt;
  735. int arg1,arg2,arg3,arg4;
  736. {
  737.     char *cp;
  738.     long t;
  739.     FILE *fp;
  740.  
  741.     if ((fp = fopen(Maillog,APPEND_TEXT)) == NULLFILE)
  742.         return;
  743.     time(&t);
  744.     cp = ctime(&t);
  745.     rip(cp);
  746.     fprintf(fp,"%s ",cp);
  747.     fprintf(fp,fmt,arg1,arg2,arg3,arg4);
  748.     fprintf(fp,"\n");
  749.     fclose(fp);
  750. }
  751.  
  752. /* send mail to a single user. Can be called from the ax24 mailbox or
  753. ** from the return mail function in the smtp client 
  754. */
  755. int
  756. mailuser(data,from,to)
  757. FILE *data;
  758. char *from;
  759. char *to;
  760. {
  761.  
  762.         int address_type, ret;
  763.         struct list *tolist = NULLLIST;
  764.  
  765.         /* check if address is ok */
  766.         if ((address_type = validate_address(to)) == BADADDR) {
  767.             return 1;
  768.         }
  769.         /* if a local address check for an alias */
  770.         if (address_type == LOCAL)
  771.             expandalias(&tolist, to);
  772.         else
  773.             /* a remote address is added to the list */
  774.             addlist(&tolist, to, address_type);
  775.         ret = mailit(data,from,tolist);
  776.         del_list(tolist);
  777.         return ret;
  778.  
  779. }
  780.